package mysql

import (
	"context"
	"errors"
	"go.uber.org/zap"
	v1 "golang-sgg-daijia/api/v1"
	"golang-sgg-daijia/internal/model"
	"gorm.io/gorm"
	"time"
)

type OrderInfoDao interface {
	Create(ctx context.Context, orderInfo *model.OrderInfo) error
	GetByID(ctx context.Context, orderID int) (*model.OrderInfo, error)
	GetByOrderNo(ctx context.Context, orderNo string) (*model.OrderInfo, error)
	UpdateByID(ctx context.Context, orderInfo *model.OrderInfo) (int64, error)
	UpdateByEqCondition(ctx context.Context, orderInfo *model.OrderInfo,
		eqConditions map[string]interface{}) (int64, error)
	FindCurrentOrder(ctx context.Context, eqConditions map[string]interface{}, status []int) (*model.OrderInfo, error)
	GetOrderNumByTime(ctx context.Context, driverID int64, startTime, endTime time.Time) (int, error)
	FindPageByCustomerID(ctx context.Context, customerID int64, page, limit int) ([]*model.OrderInfo, int64, error)
	FindPageByDriverID(ctx context.Context, customerID int64, page, limit int) ([]*model.OrderInfo, int64, error)
	GetOrderPayVo(ctx context.Context, orderNo string, customerId int64) (*v1.OrderPayVo, error)
}

func NewOrderInfoDao(
	r *MysqlClient,
) OrderInfoDao {
	return &orderInfoDao{
		MysqlClient: r,
	}
}

type orderInfoDao struct {
	*MysqlClient
}

func (r *orderInfoDao) Create(ctx context.Context, orderInfo *model.OrderInfo) error {
	if err := r.DB(ctx).Create(orderInfo).Error; err != nil {
		r.logger.WithContext(ctx).Error("orderInfoDao|Create", zap.Error(err))
		return err
	}
	return nil
}

func (r *orderInfoDao) GetByID(ctx context.Context, orderID int) (*model.OrderInfo, error) {
	orderInfo := &model.OrderInfo{}
	if err := r.DB(ctx).Where("id = ?", orderID).Where("is_deleted", 0).First(orderInfo).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		r.logger.WithContext(ctx).Error("orderInfoDao|GetByID", zap.Error(err))
		return nil, err
	}
	return orderInfo, nil
}

func (r *orderInfoDao) GetByOrderNo(ctx context.Context, orderNo string) (*model.OrderInfo, error) {
	orderInfo := &model.OrderInfo{}
	if err := r.DB(ctx).Where("order_no = ?", orderNo).Where("is_deleted", 0).First(orderInfo).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		r.logger.WithContext(ctx).Error("orderInfoDao|GetByOrderNo", zap.Error(err))
		return nil, err
	}
	return orderInfo, nil
}

func (r *orderInfoDao) UpdateByID(ctx context.Context, orderInfo *model.OrderInfo) (int64, error) {
	db := r.DB(ctx).Where("id = ?", orderInfo.ID).Updates(orderInfo)
	n, err := db.RowsAffected, db.Error
	if err != nil {
		r.logger.WithContext(ctx).Error("orderInfoDao|UpdateByID", zap.Error(err))
		return 0, err
	}
	return n, nil
}

// UpdateByEqCondition 根据等于条件更新,将更新orderInfo中非零值的字段
func (r *orderInfoDao) UpdateByEqCondition(ctx context.Context, orderInfo *model.OrderInfo,
	eqConditions map[string]interface{}) (int64, error) {

	db := r.DB(ctx).Where(eqConditions).Updates(orderInfo)
	n, err := db.RowsAffected, db.Error
	if err != nil {
		r.logger.WithContext(ctx).Error("orderInfoDao|UpdateByEqCondition", zap.Error(err))
		return 0, err
	}
	return n, nil
}

func (r *orderInfoDao) FindCurrentOrder(ctx context.Context, eqConditions map[string]interface{}, status []int) (*model.OrderInfo, error) {
	orderInfo := &model.OrderInfo{}
	db := r.DB(ctx).Where(eqConditions).Where("status in ?", status).Where("is_deleted", 0).Order("id desc").Limit(1).First(&orderInfo)
	if err := db.Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		r.logger.WithContext(ctx).Error("orderInfoDao|FindCurrentOrder", zap.Error(err))
		return nil, err
	}
	return orderInfo, nil
}

func (r *orderInfoDao) GetOrderNumByTime(ctx context.Context, driverID int64, startTime, endTime time.Time) (int, error) {
	var count int64
	err := r.DB(ctx).Model(&model.OrderInfo{}).Where("driver_id = ?", driverID).
		Where("start_service_time >= ?", startTime).Where("is_deleted", 0).Where("start_service_time <= ?", endTime).
		Count(&count).Error
	if err != nil {
		r.logger.WithContext(ctx).Error("orderInfoDao|GetOrderNumByTime", zap.Error(err))
		return 0, err
	}
	return int(count), nil
}

func (r *orderInfoDao) FindPageByCustomerID(ctx context.Context, customerID int64, page, limit int) ([]*model.OrderInfo, int64, error) {
	orderInfoList := make([]*model.OrderInfo, limit)
	var total int64
	err := r.DB(ctx).Where("customer_id = ?", customerID).Where("is_deleted", 0).Count(&total).Order("create_time desc").
		Limit(limit).Offset((page - 1) * limit).Find(&orderInfoList).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, 0, nil
		}
		r.logger.WithContext(ctx).Error("orderInfoDao|FindByCustomerIDPage", zap.Error(err))
	}

	return orderInfoList, total, nil
}

func (r *orderInfoDao) FindPageByDriverID(ctx context.Context, driverID int64, page, limit int) ([]*model.OrderInfo, int64, error) {
	orderInfoList := make([]*model.OrderInfo, limit)
	var total int64
	err := r.DB(ctx).Where("driver_id = ?", driverID).Where("is_deleted", 0).Count(&total).Order("create_time desc").
		Limit(limit).Offset((page - 1) * limit).Find(&orderInfoList).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, 0, nil
		}
		r.logger.WithContext(ctx).Error("orderInfoDao|FindPageByDriverID", zap.Error(err))
	}

	return orderInfoList, total, nil
}

func (r *orderInfoDao) GetOrderPayVo(ctx context.Context, orderNo string, customerId int64) (*v1.OrderPayVo, error) {
	result := &v1.OrderPayVo{}
	err := r.DB(ctx).Table("order_info").Select("order_info.id as order_id, order_info.customer_id, " +
		"order_info.driver_id, order_info.order_no, order_info.start_location, order_info.end_location, order_info.status, " +
		"order_bill.pay_amount, order_bill.coupon_amount").
		Joins("left join order_bill on order_info.id = order_bill.order_id").
		Where("order_info.order_no = ?", orderNo).Where("order_info.customer_id = ?", customerId).
		Scan(result).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		r.logger.WithContext(ctx).Error("orderInfoDao|SelectOrderPayVo", zap.Error(err))
		return nil, err
	}
	if result != nil {
		result.Content = result.StartLocation + " 到 " + result.EndLocation
	}
	return result, nil
}